﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.IO;
using System.Diagnostics;
using System.Xml;
using System.Xml.Schema;
using System.Threading;

namespace FileSystemItemSorter
{
    public partial class MainForm : Form
    {
        private Random random = new Random();
        public MainForm()
        {
            InitializeComponent();
        }

        private void refreshAllDrivers()
        {
            cbDrive.Items.Clear();
            List<String> allowDriveFormatList = new List<String>() { "FAT", "FAT16", "FAT32" };
            cbDrive.Items.Add("--请选择驱动器--");
            foreach (var item in DriveInfo.GetDrives())
            {
                if (item.IsReady
                    && (!cbJustUDisk.Checked || item.DriveType == DriveType.Removable)
                    && allowDriveFormatList.Contains(item.DriveFormat))
                {
                    cbDrive.Items.Add(item);
                }
            }
            cbDrive.SelectedIndex = 0;
        }

        private void MainForm_Load(object sender, EventArgs e)
        {
            refreshAllDrivers();
        }

        private Int32 executeYafs(String driveName, String xmlFileName, String other)
        {
            ProcessStartInfo psi = new ProcessStartInfo();
            psi.FileName = Path.Combine(Application.StartupPath, "yafs/yafs.exe");
            psi.Arguments = String.Format(" -d {0} -f {1}{2}", driveName, xmlFileName, other);
            
            psi.WorkingDirectory = Path.Combine(Application.StartupPath, "yafs");
            psi.WindowStyle = ProcessWindowStyle.Hidden;

            DriveInfo driveInfo = new DriveInfo(driveName);

            //如果是指定的驱动器不是可移动磁盘，且是Vista及以上操作系统
            if (driveInfo.DriveType != DriveType.Removable && System.Environment.OSVersion.Version.Major >= 6)
            {
                psi.Verb = "runas";
            }
            Process process = Process.Start(psi);
            process.WaitForExit();
            return process.ExitCode;
        }

        private void refreshDriverInfo()
        {
            if (cbDrive.SelectedIndex <= 0)
            {
                tvFileSystemItem.Nodes.Clear();
                return;
            }
            DriveInfo di = cbDrive.SelectedItem as DriveInfo;

            String tmpFileName = Path.GetTempFileName();
            String driveName = di.Name.Substring(0, 2);

            //读取文件分配表信息
            Int32 exitCode = -1;
            try
            {
                exitCode = executeYafs(driveName, tmpFileName, " -r");
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
            //如果成功
            if (exitCode == 0)
            {
                String fatXml = File.ReadAllText(tmpFileName, Encoding.UTF8);
                XmlDocument document = new XmlDocument();
                document.LoadXml(fatXml);
                tvFileSystemItem.Nodes.Clear();
                bindData(document.DocumentElement.ChildNodes, tvFileSystemItem.Nodes);
                tvFileSystemItem.CollapseAll();
            }
            else
            {
                MessageBox.Show(String.Format("读取驱动器[{0}]的文件分配表失败，请关闭相关的程序，然后重试！", driveName));
            }
            File.Delete(tmpFileName);
        }

        private void cbDrive_SelectedIndexChanged(object sender, EventArgs e)
        {
            refreshDriverInfo();
        }

        private void bindData(XmlNodeList nodeList, TreeNodeCollection tnc)
        {
            foreach (XmlNode node in nodeList)
            {
                XmlElement element = node as XmlElement;
                if (element == null) continue;
                Boolean isFolder = false;
                Boolean isFile = false;

                if ("directory".Equals(node.Name))
                {
                    isFolder = true;
                }
                else if ("file".Equals(node.Name))
                {
                    isFile = true;
                }

                if (isFolder || isFile)
                {
                    FileSystemItem fileSystemItem = new FileSystemItem(element);
                    TreeNode newTreeNode = tnc.Add(fileSystemItem.ShortName, fileSystemItem.DisplayName, fileSystemItem.ImageType, fileSystemItem.ImageType);
                    newTreeNode.Tag = fileSystemItem;
                    if (isFolder)
                    {
                        bindData(element.ChildNodes, newTreeNode.Nodes);
                    }
                }
            }
        }

        private TreeNodeCollection getTreeNodeTNC(TreeNode tn)
        {
            TreeNodeCollection tnc = null;
            if (tn.Parent == null)
                tnc = tn.TreeView.Nodes;
            else
                tnc = tn.Parent.Nodes;
            return tnc;
        }

        private void moveTreeNode(TreeNode tn, Int32 index)
        {
            TreeNodeCollection tnc = getTreeNodeTNC(tn);
            tnc.Remove(tn);
            tnc.Insert(index, tn);
            btnSaveIndex.Enabled = true;
            tn.TreeView.SelectedNode = tn;
            tn.TreeView.Focus();
        }

        private void btnUp_Click(object sender, EventArgs e)
        {
            TreeNode tn = tvFileSystemItem.SelectedNode;
            if (tn == null || tn.PrevNode == null) return;
            moveTreeNode(tn, tn.Index - 1);
        }

        private void btnDown_Click(object sender, EventArgs e)
        {
            TreeNode tn = tvFileSystemItem.SelectedNode;
            if (tn == null || tn.NextNode == null) return;
            moveTreeNode(tn, tn.Index + 1);
        }

        private void saveAllFileSystemItemOrder()
        {
            btnSaveIndex.Enabled = false;

            String tmpFileName = Path.GetTempFileName();
            XmlWriter writer = XmlWriter.Create(tmpFileName, new XmlWriterSettings() { Encoding = new UTF8Encoding(false) });
            writer.WriteStartDocument();
            writer.WriteStartElement("root");

            writeXml(writer, tvFileSystemItem.Nodes);

            writer.WriteEndElement();
            writer.WriteEndDocument();
            writer.Close();

            DriveInfo di = cbDrive.SelectedItem as DriveInfo;
            String driveName = di.Name.Substring(0, 2);
            //写入文件分配表信息
            Int32 exitCode = -1;
            try
            {
                exitCode = executeYafs(driveName, tmpFileName, " -w");
            }
            catch(Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
            //如果失败
            if (exitCode != 0)
            {
                MessageBox.Show(String.Format("写入驱动器[{0}]的文件分配表失败，请关闭相关的程序，然后重试！", driveName));
                btnSaveIndex.Enabled = true;
            }
            File.Delete(tmpFileName);
        }

        private void writeXml(XmlWriter writer, TreeNodeCollection tnc)
        {
            for (int i = 0; i < tnc.Count; i++)
            {
                Int32 currentOrder = (i + 1) * 100;
                TreeNode tn = tnc[i];
                FileSystemItem item = tn.Tag as FileSystemItem;
                String tagName = item.IsFolder ? "directory" : "file";
                {
                    writer.WriteStartElement(tagName);
                    writer.WriteAttributeString("order", currentOrder.ToString());
                    if (!String.IsNullOrEmpty(item.LongName))
                        writer.WriteElementString("long_name", item.LongName);
                    writer.WriteElementString("short_name", item.ShortName);
                    if (item.IsFolder)
                    {
                        writeXml(writer, tn.Nodes);
                    }
                    writer.WriteEndElement();
                }
            }
        }

        private void btnRefresh_Click(object sender, EventArgs e)
        {
            refreshDriverInfo();
        }

        private void lvFileSystemItem_SelectedIndexChanged(object sender, EventArgs e)
        {
            Boolean isSelected = tvFileSystemItem.SelectedNode != null;
            btnUp.Enabled = isSelected;
            btnDown.Enabled = isSelected;
        }

        private void btnSaveIndex_Click(object sender, EventArgs e)
        {
            saveAllFileSystemItemOrder();
        }

        private void tvFileSystemItem_AfterSelect(object sender, TreeViewEventArgs e)
        {
            TreeNode tn = e.Node;
            Boolean isSelected = tn != null;
            btnUp.Enabled = isSelected;
            btnDown.Enabled = isSelected;
            if (!isSelected) return;

            btnUp.Enabled = tn.PrevNode != null;
            btnDown.Enabled = tn.NextNode != null;
        }

        private void btnRefreshAllDrivers_Click(object sender, EventArgs e)
        {
            refreshAllDrivers();
        }

        private void cbJustUDisk_CheckedChanged(object sender, EventArgs e)
        {
            refreshAllDrivers();
        }

        private void btnAbout_Click(object sender, EventArgs e)
        {
            new AboutForm().ShowDialog();
        }

        private void sortTreeNodeCollection(TreeNodeCollection tnc, TreeNodeCollectionSorter sorter)
        {
            btnSortByFileName.Enabled = false;
            btnSortByRandom.Enabled = false;
            foreach (TreeNode tn in tnc)
            {
                sorter.Invoke(tn.Nodes);
            }
            sorter.Invoke(tnc);
            btnSortByFileName.Enabled = true;
            btnSortByRandom.Enabled = true;
            btnSaveIndex.Enabled = true;
        }

        public delegate void TreeNodeCollectionSorter(TreeNodeCollection tnc);

        private void btnSortByFileName_Click(object sender, EventArgs e)
        {
            sortTreeNodeCollection(tvFileSystemItem.Nodes, new TreeNodeCollectionSorter(delegate(TreeNodeCollection tnc)
                {
                    List<TreeNode> list = new List<TreeNode>();
                    foreach (TreeNode tn in tnc)
                        list.Add(tn);
                    list.Sort(new Comparison<TreeNode>(delegate(TreeNode a, TreeNode b)
                        {
                            FileSystemItem itemA = a.Tag as FileSystemItem;
                            FileSystemItem itemB = b.Tag as FileSystemItem;
                            return itemA.DisplayName.CompareTo(itemB.DisplayName);
                        }));
                    tnc.Clear();
                    foreach (TreeNode tn in list)
                    {
                        tnc.Add(tn);
                    }
                }));
        }

        private void btnSortByRandom_Click(object sender, EventArgs e)
        {
            sortTreeNodeCollection(tvFileSystemItem.Nodes, new TreeNodeCollectionSorter(delegate(TreeNodeCollection tnc)
            {
                List<TreeNode> list = new List<TreeNode>();
                Dictionary<TreeNode, Int32> dict = new Dictionary<TreeNode, int>();
                foreach (TreeNode tn in tnc)
                {
                    list.Add(tn);
                    dict.Add(tn, random.Next(Int32.MinValue, Int32.MaxValue));
                }


                list.Sort(new Comparison<TreeNode>(delegate(TreeNode a, TreeNode b)
                {
                    if (a == null && b == null) return 0;
                    if (a == null) return -1;
                    if (b == null) return 1;
                    if (a.Equals(b)) return 0;
                    FileSystemItem itemA = a.Tag as FileSystemItem;
                    FileSystemItem itemB = b.Tag as FileSystemItem;
                    if (itemA.Equals(itemB)) return 0;
                    return dict[a].CompareTo(dict[b]);
                }));
                tnc.Clear();
                foreach (TreeNode tn in list)
                {
                    tnc.Add(tn);
                }
            }));
        }
    }
}
